#ifndef __OPCODE_DEFINE__
#define __OPCODE_DEFINE__

#include "primitives.hpp"
#include <vector>

namespace swf
{
	namespace abc
	{
		typedef uint32_t num32;

		enum opcode
		{
			Unused_00, Unused_01,
			Nop,
			Throw,
			GetSuper,
			SetSuper,
			DefaultXmlNamespace,
			DefaultXmlNamespaceLate,
			Kill,
			Label,
			Unused_0A, Unused_0B,
			IfNotLowerThan,
			IfNotLowerEqual,
			IfNotGreaterThan,
			IfNotGreaterEqual,
			Jump,
			IfTrue,
			IfFalse,
			IfEqual,
			IfNotEqual,
			IfLowerThan,
			IfLessEqual,
			IfGreaterThan,
			IfGreaterEqual,
			IfStrictEqual,
			IfStrictNotEqual,
			LookupSwitch,
			PushWith,
			PopScope,
			NextName,
			HasNext,
			PushNull,
			PushUndefined,
			Unused_22,
			NextValue,
			PushByte,
			PushShort,
			PushTrue,
			PushFalse,
			PushNaN,
			Pop,
			Dup,
			Swap,
			PushString,
			PushInt,
			PushUInt,
			PushDouble,
			PushScope,
			PushNamespace,
			HasNext2,
			Unused_33, Unused_34, Unused_35, Unused_36,
			Unused_37, Unused_38, Unused_39, Unused_3A,
			Unused_3B, Unused_3C, Unused_3D, Unused_3E,
			Unused_3F,
			NewFunction,
			Call,
			Construct,
			CallMethod,
			CallStatic,
			CallSuper,
			CallProperty,
			ReturnVoid,
			ReturnValue,
			ConstructSuper,
			ConstructProperty,
			Unused_4B,
			CallPropertyLex,
			Unused_4D,
			CallSuperVoid,
			CallPropertyVoid,
			Unused_50, Unused_51, Unused_52, Unused_53,
			Unused_54,
			NewObject,
			NewArray,
			NewActivation,
			NewClass,
			GetDescendants,
			NewCatch,
			Unused_5B, Unused_5C,
			FindPropertyStrict,
			FindProperty,
			Unused_5F,
			GetLex,
			SetProperty,
			GetLocal,
			SetLocal,
			GetGlobalScope,
			GetScopeObject,
			GetProperty,
			Unused_67,
			InitProperty,
			Unused_69,
			DeleteProperty,
			Unused_6B,
			GetSlot,
			SetSlot,
			GetGlobalSlot,
			SetGlobalSlot,
			ConvertToString,
			EsccapeElement,
			EsccapeXmlAttribute,
			ConvertToInt,
			ConvertToUInt,
			ConvertToDouble,
			ConvertToBoolean,
			ConvertToObject,
			Checkfilter,
			Unused_79, Unused_7A, Unused_7B, Unused_7C,
			Unused_7D, Unused_7E, Unused_7F,
			Coerce,
			Unused_81,
			CoerceAny,
			Unused_83, Unused_84,
			CoerceString,
			AsType,
			AsTypeLate,
			Unused_88, Unused_89, Unused_8A, Unused_8B,
			Unused_8C, Unused_8D, Unused_8E, Unused_8F,
			Negate,
			Increment,
			IncrementLocal,
			Decrement,
			Unused_94,
			TypeOf,
			Not,
			BitNot,
			Unused_98, Unused_99, Unused_9A, Unused_9B,
			Unused_9C, Unused_9D, Unused_9E, Unused_9F,
			Add,
			Subtract,
			Multiply,
			Divide,
			Modulo,
			LeftShift,
			RightShift,
			UnsignedRightShift,
			BitAnd,
			BitOr,
			BitXor,
			Equals,
			StrictEquals,
			LessThan,
			LessEquals,
			GreaterThan,
			GreaterEquals,
			InstanceOf,
			IsType,
			IsTypeLate,
			In,
			Unused_B5, Unused_B6, Unused_B7, Unused_B8,
			Unused_B9, Unused_BA, Unused_BB, Unused_BC,
			Unused_BD, Unused_BE, Unused_BF,
			IncrementInt,
			DecrementInt,
			IncrementLocalInt,
			DecrementLocalInt,
			NegateInt,
			AddInt,
			SubtractInt,
			MultiplyInt,
			Unused_C8, Unused_C9, Unused_CA, Unused_CB,
			Unused_CC, Unused_CD, Unused_CE, Unused_CF,
			GetLocal0,
			GetLocal1,
			GetLocal2,
			GetLocal3,
			SetLocal0,
			SetLocal1,
			SetLocal2,
			SetLocal3,
			Unused_D8, Unused_D9, Unused_DA, Unused_DB,
			Unused_DC, Unused_DD, Unused_DE, Unused_DF,
			Unused_E0, Unused_E1, Unused_E2, Unused_E3,
			Unused_E4, Unused_E5, Unused_E6, Unused_E7,
			Unused_E8, Unused_E9, Unused_EA, Unused_EB,
			Unused_EC, Unused_ED, Unused_EE,
			Debug,
			DebugLine,
			DebugFile,
			Unused_F2, Unused_F3, Unused_F4, Unused_F5,
			Unused_F6, Unused_F7, Unused_F8, Unused_F9,
			Unused_FA, Unused_FB, Unused_FC, Unused_FD,
			Unused_FE, Unused_FF
		};

		enum ParameterType
		{
			U8,
			S24,
			U30,
			UInt,
			Dynamic
		};

		struct op_info_t
		{
			int			code;
			const int*	argdef;
		};
		struct op_info2_t
		{
			opcode		code;
			const int*	argdef;
		};

		static const int op_no_args [] = {0};
		static const int op_args_4 [] = { 1, U30 };			static const int op_args_5 [] = { 1, U30 };			static const int op_args_6 [] = { 1, U30 };
		static const int op_args_8 [] = { 1, U30 };			static const int op_args_c [] = { 1, S24 };			static const int op_args_d [] = { 1, S24 };
		static const int op_args_e [] = { 1, S24 };			static const int op_args_f [] = { 1, S24 };			static const int op_args_10 [] = { 1, S24 };
		static const int op_args_11 [] = { 1, S24 };		static const int op_args_12 [] = { 1, S24 };		static const int op_args_13 [] = { 1, S24 };
		static const int op_args_14 [] = { 1, S24 };		static const int op_args_15 [] = { 1, S24 };		static const int op_args_16 [] = { 1, S24 };
		static const int op_args_17 [] = { 1, S24 };		static const int op_args_18 [] = { 1, S24 };		static const int op_args_19 [] = { 1, S24 };
		static const int op_args_1a [] = { 1, S24 };		static const int op_args_1b [] = { 1, Dynamic };	static const int op_args_24 [] = { 1, U8 };
		static const int op_args_25 [] = { 1, U30 };		static const int op_args_2c [] = { 1, U30 };		static const int op_args_2d [] = { 1, U30 };
		static const int op_args_2e [] = { 1, U30 };		static const int op_args_2f [] = { 1, U30 };		static const int op_args_31 [] = { 1, U30 };
		static const int op_args_32 [] = { 2, UInt, UInt }; static const int op_args_40 [] = { 1, U30 };		static const int op_args_41 [] = { 1, U30 };
		static const int op_args_42 [] = { 1, U30 };		static const int op_args_43 [] = { 2, U30, U30 };	static const int op_args_44 [] = { 2, U30, U30 };
		static const int op_args_45 [] = { 2, U30, U30 };	static const int op_args_46 [] = { 2, U30, U30 };	static const int op_args_49 [] = { 1, U30 };
		static const int op_args_4a [] = { 2, U30, U30 };	static const int op_args_4c [] = { 2, U30, U30 };	static const int op_args_4d [] = { 2, U30, U30 };
		static const int op_args_4e [] = { 2, U30, U30 };	static const int op_args_4f [] = { 2, U30, U30 };	static const int op_args_53 [] = { 1, U30 };
		static const int op_args_55 [] = { 1, U30 };		static const int op_args_56 [] = { 1, U30 };
		static const int op_args_58 [] = { 1, U30 };		static const int op_args_59 [] = { 1, U30 };		static const int op_args_5a [] = { 1, U30 };
		static const int op_args_5d [] = { 1, U30 };		static const int op_args_5e [] = { 1, U30 };		static const int op_args_60 [] = { 1, U30 };
		static const int op_args_61 [] = { 1, U30 };		static const int op_args_62 [] = { 1, U30 };		static const int op_args_63 [] = { 1, U30 };
		static const int op_args_65 [] = { 1, U8 };			static const int op_args_66 [] = { 1, U30 };		static const int op_args_68 [] = { 1, U30 };
		static const int op_args_6a [] = { 1, U30 };		static const int op_args_6c [] = { 1, U30 };		static const int op_args_6d [] = { 1, U30 };
		static const int op_args_6e [] = { 1, U30 };		static const int op_args_6f [] = { 1, U30 };		static const int op_args_80 [] = { 1, U30 };
		static const int op_args_86 [] = { 1, U30 };		static const int op_args_92 [] = { 1, U30 };		static const int op_args_94 [] = { 1, U30 };
		static const int op_args_b2 [] = { 1, U30 };
		static const int op_args_c2 [] = { 1, U30 };		static const int op_args_c3 [] = { 1, U30 };		static const int op_args_ef [] = { 4, U8, U30, U8, U30 };
		static const int op_args_f0 [] = { 1, U30 };		static const int op_args_f1 [] = { 1, U30 };	
		static const int op_args_f2 [] = { 1, U30 };
		static const int* INVALIDOP = NULL;

		static op_info_t infos[256] =
		{
			{ 0x00, INVALIDOP },	{ 0x01, op_no_args },	{ 0x02, op_no_args },   { 0x03, op_no_args },
			{ 0x04, op_args_4 },	{ 0x05, op_args_5 },	{ 0x06, op_args_6 },	{ 0x07, op_no_args },
			{ 0x08, op_args_8 },	{ 0x09, op_no_args },   { 0x0a, INVALIDOP },	{ 0x0b, INVALIDOP },
			{ 0x0c, op_args_c },	{ 0x0d, op_args_d },	{ 0x0e, op_args_e },	{ 0x0f, op_args_f },
			{ 0x10, op_args_10 },   { 0x11, op_args_11 },   { 0x12, op_args_12 },   { 0x13, op_args_13 },
			{ 0x14, op_args_14 },   { 0x15, op_args_15 },   { 0x16, op_args_16 },   { 0x17, op_args_17 },
			{ 0x18, op_args_18 },   { 0x19, op_args_19 },   { 0x1a, op_args_1a },   { 0x1b, op_args_1b },
			{ 0x1c, op_no_args },   { 0x1d, op_no_args },   { 0x1e, op_no_args },   { 0x1f, op_no_args },
			{ 0x20, op_no_args },   { 0x21, op_no_args },   { 0x22, INVALIDOP },	{ 0x23, op_no_args },
			{ 0x24, op_args_24 },   { 0x25, op_args_25 },   { 0x26, op_no_args },   { 0x27, op_no_args },
			{ 0x28, op_no_args },   { 0x29, op_no_args },   { 0x2a, op_no_args },   { 0x2b, op_no_args },
			{ 0x2c, op_args_2c },   { 0x2d, op_args_2d },   { 0x2e, op_args_2e },   { 0x2f, op_args_2f },
			{ 0x30, op_no_args },   { 0x31, op_args_31 },   { 0x32, op_args_32 },   { 0x33, INVALIDOP },
			{ 0x34, INVALIDOP },	{ 0x35, INVALIDOP },	{ 0x36, INVALIDOP },	{ 0x37, INVALIDOP },
			{ 0x38, INVALIDOP },	{ 0x39, INVALIDOP },	{ 0x3a, INVALIDOP },	{ 0x3b, INVALIDOP },
			{ 0x3c, INVALIDOP },	{ 0x3d, INVALIDOP },	{ 0x3e, INVALIDOP },	{ 0x3f, INVALIDOP },
			{ 0x40, op_args_40 },   { 0x41, op_args_41 },   { 0x42, op_args_42 },   { 0x43, op_args_43 },
			{ 0x44, op_args_44 },   { 0x45, op_args_45 },   { 0x46, op_args_46 },   { 0x47, op_no_args },
			{ 0x48, op_no_args },   { 0x49, op_args_49 },   { 0x4a, op_args_4a },   { 0x4b, INVALIDOP },
			{ 0x4c, op_args_4c },   { 0x4d, op_args_4d },	{ 0x4e, op_args_4e },   { 0x4f, op_args_4f },
			{ 0x50, INVALIDOP },	{ 0x51, INVALIDOP },	{ 0x52, INVALIDOP },	{ 0x53, op_args_53 },
			{ 0x54, INVALIDOP },	{ 0x55, op_args_55 },   { 0x56, op_args_56 },   { 0x57, op_no_args },
			{ 0x58, op_args_58 },   { 0x59, op_args_59 },   { 0x5a, op_args_5a },   { 0x5b, INVALIDOP },
			{ 0x5c, INVALIDOP },	{ 0x5d, op_args_5d },   { 0x5e, op_args_5e },   { 0x5f, INVALIDOP },
			{ 0x60, op_args_60 },   { 0x61, op_args_61 },   { 0x62, op_args_62 },   { 0x63, op_args_63 },
			{ 0x64, op_no_args },   { 0x65, op_args_65 },   { 0x66, op_args_66 },   { 0x67, INVALIDOP },
			{ 0x68, op_args_68 },   { 0x69, INVALIDOP },	{ 0x6a, op_args_6a },   { 0x6b, op_no_args },
			{ 0x6c, op_args_6c },   { 0x6d, op_args_6d },   { 0x6e, op_args_6e },   { 0x6f, op_args_6f },
			{ 0x70, op_no_args },   { 0x71, op_no_args },   { 0x72, op_no_args },   { 0x73, op_no_args },
			{ 0x74, op_no_args },   { 0x75, op_no_args },   { 0x76, op_no_args },   { 0x77, op_no_args },
			{ 0x78, op_no_args },   { 0x79, INVALIDOP },	{ 0x7a, INVALIDOP },	{ 0x7b, INVALIDOP },
			{ 0x7c, INVALIDOP },	{ 0x7d, INVALIDOP },	{ 0x7e, INVALIDOP },	{ 0x7f, INVALIDOP },
			{ 0x80, op_args_80 },   { 0x81, op_no_args },	{ 0x82, op_no_args },   { 0x83, op_no_args },
			{ 0x84, op_no_args },	{ 0x85, op_no_args },   { 0x86, op_args_86 },   { 0x87, op_no_args },
			{ 0x88, op_no_args },	{ 0x89, op_no_args },	{ 0x8a, INVALIDOP },	{ 0x8b, INVALIDOP },
			{ 0x8c, INVALIDOP },	{ 0x8d, INVALIDOP },	{ 0x8e, INVALIDOP },	{ 0x8f, INVALIDOP },
			{ 0x90, op_no_args },   { 0x91, op_no_args },   { 0x92, op_args_92 },   { 0x93, op_no_args },
			{ 0x94, op_args_94 },	{ 0x95, op_no_args },   { 0x96, op_no_args },   { 0x97, op_no_args },
			{ 0x98, INVALIDOP },	{ 0x99, INVALIDOP },	{ 0x9a, op_no_args },	{ 0x9b, op_no_args },
			{ 0x9c, INVALIDOP },	{ 0x9d, INVALIDOP },	{ 0x9e, INVALIDOP },	{ 0x9f, INVALIDOP },
			{ 0xa0, op_no_args },   { 0xa1, op_no_args },   { 0xa2, op_no_args },   { 0xa3, op_no_args },
			{ 0xa4, op_no_args },   { 0xa5, op_no_args },   { 0xa6, op_no_args },   { 0xa7, op_no_args },
			{ 0xa8, op_no_args },   { 0xa9, op_no_args },   { 0xaa, op_no_args },   { 0xab, op_no_args },
			{ 0xac, op_no_args },   { 0xad, op_no_args },   { 0xae, op_no_args },   { 0xaf, op_no_args },
			{ 0xb0, op_no_args },   { 0xb1, op_no_args },   { 0xb2, op_args_b2 },   { 0xb3, op_no_args },
			{ 0xb4, op_no_args },   { 0xb5, INVALIDOP },	{ 0xb6, INVALIDOP },	{ 0xb7, INVALIDOP },
			{ 0xb8, INVALIDOP },	{ 0xb9, INVALIDOP },	{ 0xba, INVALIDOP },	{ 0xbb, INVALIDOP },
			{ 0xbc, INVALIDOP },	{ 0xbd, INVALIDOP },	{ 0xbe, INVALIDOP },	{ 0xbf, INVALIDOP },
			{ 0xc0, op_no_args },   { 0xc1, op_no_args },   { 0xc2, op_args_c2 },   { 0xc3, op_args_c3 },
			{ 0xc4, op_no_args },   { 0xc5, op_no_args },   { 0xc6, op_no_args },   { 0xc7, op_no_args },
			{ 0xc8, INVALIDOP },	{ 0xc9, INVALIDOP },	{ 0xca, INVALIDOP },	{ 0xcb, INVALIDOP },
			{ 0xcc, INVALIDOP },	{ 0xcd, INVALIDOP },	{ 0xce, INVALIDOP },	{ 0xcf, INVALIDOP },
			{ 0xd0, op_no_args },   { 0xd1, op_no_args },   { 0xd2, op_no_args },   { 0xd3, op_no_args },
			{ 0xd4, op_no_args },   { 0xd5, op_no_args },   { 0xd6, op_no_args },   { 0xd7, op_no_args },
			{ 0xd8, INVALIDOP },	{ 0xd9, INVALIDOP },	{ 0xda, INVALIDOP },	{ 0xdb, INVALIDOP },
			{ 0xdc, INVALIDOP },	{ 0xdd, INVALIDOP },	{ 0xde, INVALIDOP },	{ 0xdf, INVALIDOP },
			{ 0xe0, INVALIDOP },	{ 0xe1, INVALIDOP },	{ 0xe2, INVALIDOP },	{ 0xe3, INVALIDOP },
			{ 0xe4, INVALIDOP },	{ 0xe5, INVALIDOP },	{ 0xe6, INVALIDOP },	{ 0xe7, INVALIDOP },
			{ 0xe8, INVALIDOP },	{ 0xe9, INVALIDOP },	{ 0xea, INVALIDOP },	{ 0xeb, INVALIDOP },
			{ 0xec, INVALIDOP },	{ 0xed, INVALIDOP },	{ 0xee, INVALIDOP },	{ 0xef, op_args_ef },
			{ 0xf0, op_args_f0 },   { 0xf1, op_args_f1 },   { 0xf2, op_args_f2 },	{ 0xf3, INVALIDOP },
			{ 0xf4, INVALIDOP },	{ 0xf5, INVALIDOP },	{ 0xf6, INVALIDOP },	{ 0xf7, INVALIDOP },
			{ 0xf8, INVALIDOP },	{ 0xf9, INVALIDOP },	{ 0xfa, INVALIDOP },	{ 0xfb, INVALIDOP },
			{ 0xfc, INVALIDOP },	{ 0xfd, INVALIDOP },	{ 0xfe, INVALIDOP },	{ 0xff, INVALIDOP },
		};

		struct instruction
		{
			const op_info2_t * opi;
			std::vector<num32> args;
			instruction() : opi(0), args(0)
			{
			}
			bool_t read( StreamIn & is )
			{
				u8 opc;
				size_t off = is.tell();
				neednz( is.read(opc) );
				//printf( "%08X : %02X\n", off, opc );
				op_info_t & oinf = infos[opc];
				if( oinf.argdef == INVALIDOP ) 
					return false;
				size_t argc = oinf.argdef[0];
				for( size_t i = 0; i < argc; ++ i )
				{
					int argt = oinf.argdef[1+i];
					if( argt == U8 )
					{
						u8 b;
						neednz( is.read(b) );
						args.push_back( b );
					}
					else if( argt == S24 )
					{
						s24 _24;
						neednz( _24.read(is) );
						args.push_back(_24.i);
					}
					else if( argt == U30 )
					{
						u30 _30;
						neednz( _30.read(is) );
						args.push_back(_30.i);
					}
					else if( argt == UInt )
					{
						u32 _32;
						neednz( _32.read(is) );
						args.push_back(_32.i);
					}
					else if( argt == Dynamic )
					{
						s24 d; u30 c;
						neednz( d.read(is) );
						args.push_back( d.i );
						neednz( c.read(is) );
						args.push_back( c.i );
						for( size_t i = 0; i < c.i + 1; ++ i )
						{
							s24 u;
							neednz( u.read(is) );
							args.push_back( u.i );
						}
					}
					else 
					{
						return false;
					}
				}
				opi = (op_info2_t*)&oinf;
				return true;
			}
		};
	};
};

#endif